﻿using System;
using System.Windows;
using System.ComponentModel.DataAnnotations;
using System.Windows.Data;
using System.Windows.Input;

namespace Real.Silverlight.Controls
{
    public class DatePicker : System.Windows.Controls.DatePicker
    {
        #region Fields
        private System.Windows.Controls.TextBox _datePickerTextbox;
        #endregion

        #region Properties
        public string ValidationErrorMessage
        {
            get { return (string)GetValue(ValidationErrorMessageProperty); }
            set { SetValue(ValidationErrorMessageProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ValidationErrorMessage.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ValidationErrorMessageProperty =
            DependencyProperty.Register("ValidationErrorMessage", typeof(string), typeof(DatePicker), new PropertyMetadata("Invalid input"));

        #endregion

        #region Methods
        public override void OnApplyTemplate()
        {
            _datePickerTextbox = (System.Windows.Controls.TextBox)this.GetTemplateChild("TextBox");
            _datePickerTextbox.LostFocus += OnTextBoxTextChanged;
            _datePickerTextbox.KeyDown += OnTextBoxKeyDown;

            this.SelectedDateChanged += OnSelectedDateChanged;

            base.OnApplyTemplate();
        }
      
        private void ValidateTextInTextBox()
        {
            if (!string.IsNullOrWhiteSpace(_datePickerTextbox.Text))
            {
                DateTime outDate;
                if (!DateTime.TryParse(_datePickerTextbox.Text, out outDate))
                {
                    OnDateValidationError(new System.Windows.Controls.DatePickerDateValidationErrorEventArgs(new ValidationException(ValidationErrorMessage), _datePickerTextbox.Text));
                    ValidationHelper.SetControlError(this, ValidationErrorMessage);
                    this.SelectedDate = null;
                }
            }
            else
                //valid
                ValidationHelper.ClearControlError(this);
        }
        #endregion

        #region EventHandlers
        private void OnTextBoxTextChanged(object sender, RoutedEventArgs e)
        {
            System.Windows.Controls.TextBox txt = (System.Windows.Controls.TextBox)sender;

            if (!string.IsNullOrWhiteSpace(txt.Text))
            {
                DateTime outDate;
                if (!DateTime.TryParse(txt.Text, out outDate))
                {
                    OnDateValidationError(new System.Windows.Controls.DatePickerDateValidationErrorEventArgs(new ValidationException(ValidationErrorMessage), txt.Text));
                    ValidationHelper.SetControlError(this, ValidationErrorMessage);
                    this.SelectedDate = null;
                }
            }
            else
                //valid
                ValidationHelper.ClearControlError(this);
        }

        private void OnSelectedDateChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            if (this.SelectedDate.HasValue)
                ValidationHelper.ClearControlError(this);
        }

        private void OnTextBoxKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.Enter)
            {
                ValidateTextInTextBox();
            }
        }
        #endregion

        #region (De-) constructors
        ~DatePicker()
        {
            _datePickerTextbox.TextChanged -= OnTextBoxTextChanged;
            _datePickerTextbox.KeyDown -= OnTextBoxKeyDown;
            this.SelectedDateChanged -= OnSelectedDateChanged;
        }

        #endregion
    }

    public class ValidationHelper
    {
        #region Fields
        private string _message;
        #endregion

        #region Constructor
        public ValidationHelper(string message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }

            _message = message;
            ThrowValidationError = true;
        }
        #endregion

        #region Properties
        public bool ThrowValidationError
        {
            get;
            set;
        }

        public object ValidationError
        {
            get { return null; }
            set
            {
                if (ThrowValidationError)
                {
                    throw new ValidationException(_message);
                }
            }
        }
        #endregion

        #region Static methods
        internal static void SetControlError(System.Windows.Controls.Control control, string errorMessage)
        {
            ValidationHelper validationHelper =
                new ValidationHelper(errorMessage);

            control.SetBinding(System.Windows.Controls.Control.TagProperty, new Binding("ValidationError")
            {
                Mode = BindingMode.TwoWay,
                NotifyOnValidationError = true,
                ValidatesOnExceptions = true,
                UpdateSourceTrigger = UpdateSourceTrigger.Explicit,
                Source = validationHelper
            });

            // throws validation exception through Tag
            control.GetBindingExpression(System.Windows.Controls.Control.TagProperty).UpdateSource();
        }

        internal static void ClearControlError(System.Windows.Controls.Control control)
        {
            BindingExpression b = control.GetBindingExpression(System.Windows.Controls.Control.TagProperty);

            if (b != null)
            {
                ((ValidationHelper)b.DataItem).ThrowValidationError = false;
                b.UpdateSource();
            }
        }

        #endregion
    }
}